<?php

/**
 * bbcode核心类
 * 支持标准BBCODE标签
 *
 * @author jonwang(jonwang@myqee.com)
 * @category   MyQEE
 * @package	System
 * @subpackage Core
 * @copyright  Copyright (c) 2008-2011 myqee.com
 * @license	http://www.myqee.com/license.html
 */
abstract class MyQEE_bbcode
{

    /**
     * @name 显示最大引用层级，0为不限制
     * @var int
     */
    public static $max_nested_quotes = 3;

    /**
     * @name 标签替换字符串
     * @var array $tag
     */
    protected static $tag = array(
        'a' => array(
        	'<a href="$1" rel="nofollow" target="_blank">$2</a>',
        	'<!--deny_url_v2 $1--><u style="color:#666;font-weight:bold;">$2</u> <span style="font-size:10px;color:#999">($3)</span><!--End deny_url_v2-->'
        ), 
        'code' => '<div class="ubb_code"><pre class="brush: $2">$1</pre></div>', 
        'quote' => array(
            '<div class="quotetop">引用</div><div class="quotemain">$1</div>', 
            '<span><div class="quotetop">引用</div><div class="quotemain">部分楼层已被隐藏，<a href="#" onclick="this.style.display = \'none\';this.parentNode.parentNode.style.display = \'none\';var obj=document.getElementById(\'$1\');if(obj)obj.style.display = \'\';return false;" style="color:#B22222;text-decoration:underline">点击展开全部</a></div></span>', 
            '<div class="quotetop">引用($name @ $date) <a href="/topics/$folder_id/comments/comment$comment_id"><img border="0" alt="*" src="http://doc.VeryCD.com/style_images/1/post_snapback.gif"/></a></div><div class="quotemain">$1</div>'
        ),
    );

    protected static $_code_tmp = array();

    protected static $_quote_tmp = array();

    /**
     * 编译BBCODE
     * 将BBCODE代码转换为HTML
     * @param string $str
     * @return $str
     */
    public static function parse($str)
    {
        $str = str_replace(array('<', '>'), array('&lt;', '&gt;'), $str);
        //替换CODE标签，支持CODE嵌套
        $str = bbcode::_parse_code($str, true);
        
        preg_match_all('#\[quote([^\]]+?)\]#ie', $str, $match);
        if ( count($match[0]) )
        {
            foreach ( $match[0] as $k => $item )
            {
                $key = '{quOte:' . md5($match[1][$k]) . '}';
                bbcode::$_quote_tmp[$key] = $match[1][$k];
                $str = str_replace($item, '[quote]' . $key, $str);
            }
        }
        //把含有大写的标签全部替换成小写，便于下面操作
        $str = preg_replace('#\[(/)?(quote|a|tag)\]#ie', "strtolower('[\\1\\2]')", $str);
        
        //替换正常标签
        $str = bbcode::_parse($str, 0);
        
        //将临时替换的CODE标签还原
        $str = bbcode::_render_code($str);
        
        bbcode::$_code_tmp = array();
        return $str;
    }

    protected static function _parse($str, $sonindex = 0)
    {
        $str = bbcode::_parse_a($str);
        $str = bbcode::_parse_quote($str, $sonindex);
        
        $tRegexAry = array();
        $tRegexAry[] = array("\r\n", "<br />");
        $tRegexAry[] = array("\r", "<br />");
        $tRegexAry[] = array("\n", "<br />");
        $tRegexAry[] = array('\[p\]([^\[]*?)\[\/p\]', "\\1<br />");
        $tRegexAry[] = array('\[b\]([^\[]*?)\[\/b\]', "<b>\\1</b>");
        $tRegexAry[] = array('\[i\]([^\[]*?)\[\/i\]', "<i>\\1</i>");
        $tRegexAry[] = array('\[u\]([^\[]*?)\[\/u\]', "<u>\\1</u>");
        $tRegexAry[] = array('\[ol\]([^\[]*?)\[\/ol\]', "<ol>\\1</ol>");
        $tRegexAry[] = array('\[ul\]([^\[]*?)\[\/ul\]', "<ul>\\1</ul>");
        $tRegexAry[] = array('\[li\]([^\[]*?)\[\/li\]', "<li>\\1</li>");
        $tRegexAry[] = array('\[quote\]([^\[]*?)\[\/quote\]', "<div class=\"ubb_quote\">\\1</div>");
        $tRegexAry[] = array('\[color=([^\]]*)\]([^\[]*?)\[\/color\]', "<font style=\"color: \\1\">\\2</font>");
        $tRegexAry[] = array('\[hilitecolor=([^\]]*)\]([^\[]*?)\[\/hilitecolor\]', "<font style=\"background-color: \\1\">\\2</font>");
        $tRegexAry[] = array('\[align=([^\]]*)\]([^\[]*?)\[\/align\]', "<div style=\"text-align: \\1\">\\2</div>");
        $tRegexAry[] = array('\[img\]([^\[]*?)\[\/img\]', "<a href=\"\\1\" target=\"_blank\"><img src=\"\\1\" /></a>");
        //******************************************************************
        $tState = true;
        while ( $tState )
        {
            $tState = false;
            for( $ti = 0; $ti < count($tRegexAry); $ti ++ )
            {
                $tnRegexString = "/" . $tRegexAry[$ti][0] . "/is";
                if ( preg_match($tnRegexString, $str) )
                {
                    $tState = true;
                    $str = preg_replace($tnRegexString, $tRegexAry[$ti][1], $str);
                }
            }
        }
        return $str;
    }

    /**
     * 编译Code区域
     * 
     * CODE区域作为第一优先级，应当被最第一步执行，然后在最后输出_render_code
     * 
     * @param $str
     * @param $runfrist 是否第一次调用
     * @return $code
     */
    protected static function _parse_code($str, $runfrist = true)
    {
        if ( $runfrist === true )
        {
            $str = preg_replace('#\[(\/)?code(=[^\[\]]+)?\]#i', '[$1code$2]', $str);
        }
        if ( ! preg_match('#\[code(?:=([^\[\]]+))?\]#', $str, $m) )
        {
            return $str;
        }
        
        if ( $m[1] )
        {
            $m[1] = preg_replace('#highlight:([0-9\,]+)#', 'highlight:[$1]', $m[1]);
            $type = $m[1];
        }
        else
        {
            $type = 'html';
        }
        $arr_lt = explode($m[0], $str, 2);
        $arr_gt = explode('[/code]', $arr_lt[1]);
        $mystr = '';
        do
        {
            $c = substr_count($arr_gt[0], '[code]');
            if ( $c )
            {
                bbcode::_parse_getcode($c, $arr_gt, $mystr, 'code');
            }
            else
            {
                $mystr .= array_shift($arr_gt);
                break;
            }
        }
        while ( $arr_gt );
        
        $mystr = trim($mystr);
        $mystr = str_replace(array('$1', '$2'), array($mystr, $type), bbcode::$tag['code']);
        $md5 = '{cOde:' . md5($mystr . '^_^') . '}';
        bbcode::$_code_tmp[$md5] = $mystr;
        
        $mystr = $arr_lt[0] . $md5;
        
        //替换下一个标签
        if ( $arr_gt )
        {
            $str = implode('[/code]', $arr_gt);
            $mystr .= bbcode::_parse_code($str, false);
        }
        return $mystr;
    }

    protected static function _parse_getcode($c, &$arr2, &$mystr, $tag = 'code')
    {
        for( $i = 0; $i < $c; $i ++ )
        {
            $tmpstr = array_shift($arr2);
            $mystr .= $tmpstr . '[/' . $tag . ']';
            $c2 = substr_count($arr2[0], '[' . $tag . ']');
            if ( $c2 )
            {
                bbcode::_parse_getcode($c2, $arr2, $mystr);
            }
        }
    }

    /**
     * 替换Code区域
     * 
     * 最后输出CODE区域
     * 
     * @param $code
     * @return $code
     */
    protected static function _render_code($code)
    {
        $code = strtr($code, bbcode::$_code_tmp);
        return $code;
    }

    protected static function _parse_a($str)
    {
        $str = preg_replace('#\[url\](\S+?)\[/url\]#ie', "bbcode::_regex_url(array('html' => '\\1', 'show' => '\\1'))", $str);
        $str = preg_replace('#\[url\s*=\s*\&quot\;\s*(\S+?)\s*\&quot\;\s*\](.*?)\[\/url\]#ie', "bbcode::_regex_url(array('html' => '\\1', 'show' => '\\2'))", $str);
        $str = preg_replace('#\[url\s*=\s*(\S+?)\s*\](.*?)\[\/url\]#ie', "bbcode::_regex_url(array('html' => '\\1', 'show' => '\\2'))", $str);
        return $str;
    }

    /**
     * 
     * @param array $url array('html'=>'http://www.test.com/','show'=>'http://www.test.com/');
     */
    protected static function _regex_url($url = array())
    {
        $skip_it = 0;
        if ( ! preg_match('/^(http|ftp|https|news|ed2k):\/\//i', $url['show']) ) $skip_it = 1;
        
        $show = $url['show'];
        $show_strlen = mb_strlen($url['show'], 'utf-8');
        
        if ( $skip_it != 1 )
        {
            $stripped = preg_replace('#^(http|ftp|https|news|ed2k)://(\S+)$#i', "\\2", $url['show']);
            $uri_type = preg_replace('#^(http|ftp|https|news|ed2k)://(\S+)$#i', "\\1", $url['show']);
            
            $show = $uri_type . '://' . str::substr($stripped, 0, 35) . '...' . str::substr($stripped, $show_strlen - 15, $show_strlen);
        }
        
        $allowedly = false;
        
        if ( $allowedly )
        {
            $arr = array('$1' => $url['html'], '$2' => $show);
            $return = strtr(bbcode::$tag['a'][0], $arr);
        }
        else
        {
            $arr = array('$1' => base64_encode("[url={$url['html']}]{$url['show']}[/url]"), '$2' => $show, '$3' => $url['html']);
            $return = strtr(bbcode::$tag['a'][1], $arr);
        }
        
        return $return;
    }

    /**
     * 解析引用
     * @param string $str 待解析的字符串
     * @param int $sonindex 层级，默认0
     * @return $str
     */
    protected static function _parse_quote($str, $sonindex = 0)
    {
        if ( strpos($str, '[quote]') === false ) return $str;
        $arr_lt = explode('[quote]', $str, 2);
        $arr_gt = explode('[/quote]', $arr_lt[1]);
        $mystr = '';
        do
        {
            $c = substr_count($arr_gt[0], '[quote]');
            if ( $c )
            {
                bbcode::_parse_getcode($c, $arr_gt, $mystr, 'quote');
            }
            else
            {
                $mystr .= array_shift($arr_gt);
                break;
            }
        }
        while ( $arr_gt );
        
        $keylen = 40; //{quOte:3d4044ccae96dcbb770629a20a51ce64} len=8+32
        $keystr = substr($mystr, 0, $keylen);
        if ( array_key_exists($keystr, bbcode::$_quote_tmp) )
        {
            $mystr = substr($mystr, $keylen);
            //处理[quote]标签上的参数
            $tmpstr = bbcode::_parse_quote_tagprex(bbcode::$tag['quote'][2], bbcode::$_quote_tmp[$keystr]);
        }
        else
        {
            $tmpstr = bbcode::$tag['quote'][0];
        }
        $sonindex ++;
        $mystr = bbcode::_parse($mystr, $sonindex);
        
        if ( $sonindex > 1 && $sonindex == bbcode::$max_nested_quotes + 1 )
        {
            //隐藏第4级后面的内容
            $id = '_quote_' . md5('tmp_' . bbcode::_get_rand('20'));
            $tmpstr = '<div id="' . $id . '" style="display:none;">' . $tmpstr . '</div>';
            $tmpstr = str_replace('$1', $id, bbcode::$tag['quote'][1]) . $tmpstr;
        }
        $mystr = str_replace('$1', $mystr, $tmpstr);
        
        $mystr = $arr_lt[0] . $mystr;
        
        //替换下一个标签
        if ( $arr_gt )
        {
            $str = implode('[/quote]', $arr_gt);
            $mystr .= bbcode::_parse_quote($str, 0);
        }
        
        return $mystr;
    }

    /**
     * 处理引用tag里面的参数
     * @param string $tmpstr 替换的模板
     * @param string $extra 标签上面的字符串
     * @return $str
     */
    protected static function _parse_quote_tagprex($tmpstr, $extra)
    {
        preg_match('#post=(["\']|&quot;|&\#039;|&\#39;)?(\d+)(["\']|&quot;|&\#039;|&\#39;)?#', $extra, $match);
        if ( @intval($match[2]) )
        {
            $post_id = intval($match[2]);
            $tmpstr = str_replace('$post_id', $post_id, $tmpstr);
        }
        
        preg_match('#folder=(["\']|&quot;|&\#039;|&\#39;)?(\d+):(\d+)(["\']|&quot;|&\#039;|&\#39;)?#', $extra, $match);
        if ( @intval($match[2]) && @intval($match[3]) )
        {
            $folder_id = intval($match[2]);
            $comment_id = intval($match[3]);
            $tmpstr = str_replace('$folder_id', $folder_id, $tmpstr);
            $tmpstr = str_replace('$comment_id', $comment_id, $tmpstr);
        }
        
        preg_match('#group=(["\']|&quot;|&\#039;|&\#39;)?(\d+):(\d+):(\d+)(["\']|&quot;|&\#039;|&\#39;)?#', $extra, $match);
        if ( @intval($match[2]) && @intval($match[3]) && @intval($match[4]) )
        {
            $group_id = intval($match[2]);
            $topic_id = intval($match[3]);
            $groupPost_id = intval($match[4]);
            
            $tmpstr = str_replace('$group_id', $group_id, $tmpstr);
            $tmpstr = str_replace('$topic_id', $topic_id, $tmpstr);
            $tmpstr = str_replace('$groupPost_id', $groupPost_id, $tmpstr);
        }
        
        preg_match('#name=(["\']|&quot;|&\#039;|&\#39;)(.+?)(["\']|&quot;|&\#039;|&\#39;)\s?(date|post)#is', $extra, $match);
        if ( $match )
        {
            $name = bbcode::_make_quote_safe($match[2]);
            $tmpstr = str_replace('$name', $name, $tmpstr);
        }
        
        preg_match('#date=(["\']|&quot;|&\#039;|&\#39;)(.+?)(["\']|&quot;|&\#039;|&\#39;)#', $extra, $match);
        if ( $match )
        {
            $date = bbcode::_make_quote_safe($match[2]);
            $tmpstr = str_replace('$date', $date, $tmpstr);
        }
        return $tmpstr;
    }

    protected static function _make_quote_safe($txt = '')
    {
        $txt = str_replace(array('+', '-', ':', '[', ']', ')', '(', "'"), array('&#043;', '&#045;', '&#58;', '&#91;', '&#93;', '&#41;', '&#40;', "&#039;"), $txt);
        return $txt;
    }

    /**
     * 获取随机字符串
     * @param int $len 长度
     */
    protected static function _get_rand($len = 10)
    {
        $chr = '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
        $arr = explode(',', $chr);
        shuffle($arr);
        $str = implode('', $arr);
        $ran = rand(0, count($arr) - $len);
        $rand = substr($str, $ran, $len);
        return $rand;
    }
}